/* 
 * File:   server_SockStream.cpp
 * Author: max
 * 
 * Created on May 21, 2013, 11:42 PM
 */

#include "server_SockStream.h"
#include <errno.h>
#include <vector>

#include <iostream>
using namespace std;

SockStream::SockStream(){
    _domain = AF_INET;
    _protocol = PF_UNSPEC;
}

SockStream::SockStream(int pDomain, int pProtocol) {
    _domain = pDomain;
    _protocol = pProtocol;
}

SockStream::~SockStream() {
}

void SockStream::Iniciar () throw (Excepcion&){

    if (-1 == (_sockfd = socket(_domain, SOCK_STREAM, _protocol)))
    {
        string msg = "No se puede iniciar el socket. "; 
        msg += strerror (errno);

        throw Excepcion (msg);
    }
}

void SockStream::Listen(int pQueueLength) throw (Excepcion&) {
    
    if (-1 == listen(_sockfd,pQueueLength))
    {
       string msg = "Error en listen. ";
       msg += strerror(errno);
       throw Excepcion (msg);
    }
}

void SockStream::Finalizar() throw (Excepcion&){

    // Se eliminan los EndPoints administrados por el stream.
    //
    for (vector<SockEndPoint*>::size_type cur = 0; cur < this->_endPoints.size(); ++cur)
    {
        _endPoints[cur]->Finalizar();
        delete _endPoints[cur];
    }
    _endPoints.clear();
    
    // Se cierra la conexion.
    shutdown(_sockfd, SHUT_RDWR);
    close(_sockfd);
}

void SockStream::Bind(string& pPuerto, uint32_t pInAddr) throw (Excepcion&) {

    bzero((char *) &serv_addr, sizeof(serv_addr));
    
    this->serv_addr.sin_family = _domain;
    this->serv_addr.sin_addr.s_addr = pInAddr;
    this->serv_addr.sin_port = htons(atoi(pPuerto.c_str()));

    long bindResult = bind(_sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
    int errnoAux = errno;
    
    if (-1 == bindResult) 
    {
       string msg = "No se puede iniciar el puerto. ";
       msg += strerror (errnoAux);
       throw Excepcion (msg);
    }
}

SockEndPoint* SockStream::Acept () throw (Excepcion&){

    struct sockaddr_in* pClientAddress = (struct sockaddr_in*) new (struct sockaddr);
    
    socklen_t clilen = sizeof(pClientAddress);
    int sockEndPointFD = -1;
    
    if ((sockEndPointFD = accept(_sockfd,reinterpret_cast<struct sockaddr*> (pClientAddress), &clilen)) <= 0)
    {
        if (NULL != pClientAddress) 
        {
            delete pClientAddress;
        }
        
        string msg = "";
        descripcionError(msg, errno);
        throw Excepcion (msg);
    }
    
    SockEndPoint* endPoint = new SockEndPoint(sockEndPointFD, pClientAddress);
    _endPoints.push_back(endPoint);
    
    return endPoint;
}

void SockStream::descripcionError (string &pMensaje, int pCodigoError){
 
    pMensaje = "ERROR_CONNECTION.  ";
    
    switch (pCodigoError)
    {
        case EAGAIN:
           pMensaje += "The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.";
           break;

        case EBADF:
           pMensaje += "The descriptor is invalid.";
           break;
           
        case ECONNABORTED:
           pMensaje += "A connection has been aborted."; 

        case EFAULT:
           pMensaje += "The addr argument is not in a writable part of the user address space."; 
           break;    

        case EINTR:
           pMensaje += "The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7)."; 
           break;
           
        case EINVAL:
           pMensaje += "Socket is not listening for connections, or addrlen is invalid (e.g., is negative)."; 
           pMensaje += "(accept4()) invalid value in flags."; 
           break;
           
        case EMFILE:
           pMensaje += "The per-process limit of open file descriptors has been reached."; 
           pMensaje += "The system limit on the total number of open files has been reached."; 
           break;

        case ENOBUFS:
        case ENOMEM:
           pMensaje += "Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory."; 
           break;

        case ENOTSOCK:
           pMensaje += "The descriptor references a file, not a socket."; 
           break;

        case EOPNOTSUPP:
            pMensaje += "The referenced socket is not of type SOCK_STREAM."; 
            break;

        case EPROTO:
            pMensaje += "Protocol error.";
            break;

        case EPERM:
            pMensaje += "Firewall rules forbid connection.";
            break;
        default:
            pMensaje += "Desconocido.";
    }
 
}

